#!/usr/bin/python
#-*- coding: utf-8 -*-

import urllib
import urllib2
import urlparse
import gzip
from StringIO import StringIO
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers


USER_AGENT = 'Mozilla/5.0'


class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
    """
    class for handling redirect
    Additional status is added in the result
    """
    def http_error_301(self, req, fp, code, msg, headers):
        """
        function for handling http 301 error
        Additional status is added in the result
        """
        result = urllib2.HTTPRedirectHandler.http_error_301(
                self, req, fp, code, msg, headers)
        result.status = code
        return result

    def http_error_302(self, req, fp, code, msg, headers):
        """
        function for handling http 302 error
        Additional status is added in the result
        """
        result = urllib2.HTTPRedirectHandler.http_error_302(
                self, req, fp, code, msg, headers)
        result.status = code
        return result


class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler):
    """
    class for handling default http error
    Additional status is added in the result
    """
    def http_error_default(self, req, fp, code, msg, headers):
        """
        function for handling default http error
        Additional status is added in the result
        """
        result = urllib2.HTTPError(
                req.get_full_url(), code, msg, headers, fp)
        result.status = code
        return result


def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    """
    function for opening source (anysource)
    source: url or other source
    etag: check the etag
    lastmodified: check if updated
    agent: user_agent
    In most case, the source is the request, this function can open the
    given request specified in source.
    """
    if hasattr(source, 'read'):
        return source

    if source == '-':
        return sys.stdin

    if urlparse.urlparse(source)[0] in ['http', 'https']:
        #open URL with urllib2
        request = urllib2.Request(source)
        request.add_header('User-Agent', agent)
        if etag:
            request.add_header('If-None-Match', etag)
        if lastmodified:
            request.add_header('If-Modified-Since', lastmodified)
        request.add_header('Accept-encoding', 'gzip')
        opener = urllib2.build_opener(SmartRedirectHandler(),\
                                      DefaultErrorHandler())
        return opener.open(request)


def fetch(source, etag=None, lastmodified=None, agent=USER_AGENT):
    """
    function for fetching data from source
    source: url or other source
    etag: check the etag
    lastmodified: check if updated
    agent: user_agent

    Typically, it is used for fetching json or xml or html from http
    service.
    """
    result = {}
    f = openAnything(source, etag, lastmodified, agent)
    result['data'] = f.read()
    if hasattr(f, 'headers'):
        result['etag'] = f.headers.get('ETag')
        result['lastmodified'] = f.headers.get('Last-Modified')
        if f.headers.get('content-encoding', '') == 'gzip':
            result['data'] = gzip.GzipFile(\
                fileobj=StringIO(result['data'])).read()
    if hasattr(f, 'url'):
        result['url'] = f.url
        result['status'] = 200
    if hasattr(f, 'status'):
        result['status'] = f.status
    f.close()
    return result


def basicAPIFetch(host, url, params={}, SSL=False, method='GET', \
                  etag=None, lastmodified=None, multipart=False):
    """
    function for fetch data from a api
    SSL: whether to use SSL, false -> 'http', true -> 'https'
    host: host for the api
    url: url for the api
    params: parameter list for the api
    method: http method. one of 'GET', 'POST'
    etag: check the etag
    lastmodified: check if updated

    This is the basic function for fetch a data for an api. It will
    be common used for all web api.
    """
    # construct request
    baseUrl = ''
    if SSL:
        baseUrl += 'https://'
    else:
        baseUrl += 'http://'
    baseUrl = baseUrl + host + url
    if multipart:
        register_openers()
        datagen, headers = multipart_encode(params)
        request = urllib2.Request(baseUrl, datagen, headers)
    else:
        data = urllib.urlencode(params)
        if method == 'GET':
            fullUrl = baseUrl + '?' + data
            request = urllib2.Request(fullUrl)
        elif method == 'POST':
            request = urllib2.Request(baseUrl, data)
    request.add_header('User-Agent', USER_AGENT)
    if etag:
        request.add_header('If-None-Match', etag)
    if lastmodified:
        request.add_header('If-Modified-Since', lastmodified)
    request.add_header('Accept-encoding', 'gzip')
    if multipart:
        f = urllib2.urlopen(request)
    else:
        opener = urllib2.build_opener(SmartRedirectHandler(),\
                                  DefaultErrorHandler())
        f = opener.open(request)
    result = {}
    result['data'] = f.read()
    if hasattr(f, 'headers'):
        result['etag'] = f.headers.get('ETag')
        result['lastmodified'] = f.headers.get('Last-Modified')
        if f.headers.get('content-encoding', '') == 'gzip':
            result['data'] = gzip.GzipFile(\
                fileobj=StringIO(result['data'])).read()
    if hasattr(f, 'url'):
        result['url'] = f.url
        result['status'] = 200
    if hasattr(f, 'status'):
        result['status'] = f.status
    f.close()
    return result


if __name__ == '__main__':
    # test the basic api with google direction api
    host = 'maps.googleapis.com'
    url = '/maps/api/directions/json'
    data = {'origin': 'ntu,Singapore', 'destination': 'nus,Singapore',\
            'sensor': 'false'}
    res = basicAPIFetch(host, url, data)
    print res['status']
    print res['data']
